#include <glib.h>
#include <glib/gprintf.h>
#include <unistd.h>
#include <math.h>
#include <stdlib.h>
#include "logging.h"

GIOChannel * mp_in;
GIOChannel * mp_out;    
GPid child_pid;
gboolean paused = FALSE;
gboolean pausing_input = FALSE;
gdouble current_time;
GMutex * inmutex;
void (*time_callback)(GString * new_time);
void (*pause_callback)(gboolean status);

void mplayerc_ask_all() {
    g_mutex_lock(inmutex);
    g_io_channel_write_chars(mp_in, "pausing_keep_force get_property time_pos\n", -1, NULL, NULL);
    g_io_channel_write_chars(mp_in, "pausing_keep_force get_property pause\n", -1, NULL, NULL);
    g_io_channel_flush(mp_in, NULL);
    g_mutex_unlock(inmutex);
}

void mplayerc_seek(double new_pos) {
    gchar * message;
    g_mutex_lock(inmutex);
    if (paused) {
        message = g_strdup_printf ("pausing_keep seek %f 2\n", new_pos);
    } else {
        message = g_strdup_printf ("pausing_keep_force seek %f 2\n", new_pos);
    }
    g_io_channel_write_chars(mp_in, message, -1, NULL, NULL);
    g_free(message);
    g_io_channel_flush(mp_in, NULL);
    g_mutex_unlock(inmutex);
}

void mplayerc_sync_timing(double new_pos, double accuracy) {
    mplayerc_ask_all();
    g_debug("Time diff: %f, %f, %f", current_time, new_pos, fabs(current_time - new_pos));
    if (fabs(current_time - new_pos) > accuracy) {
        g_debug("Out of Sync - Skipping! :(");
        mplayerc_seek(new_pos);
    }
}
void mplayerc_pause(gboolean status) {
    g_mutex_lock(inmutex);
    pausing_input = status;
    if (paused != status) {
        g_io_channel_write_chars(mp_in, "pause\n", -1, NULL, NULL);
    }
    g_io_channel_flush(mp_in, NULL);
    g_mutex_unlock(inmutex);
}

void mplayerc_quit() {
    g_mutex_lock(inmutex);
    g_io_channel_write_chars(mp_in, "quit\n", -1, NULL, NULL);
    g_io_channel_flush(mp_in, NULL);
    g_mutex_unlock(inmutex);
}

gpointer mplayerc_auto(gpointer data) {
    while (TRUE) {
        mplayerc_ask_all();
        g_usleep(*(gint *)data);
    }
}

void mplayerc_start_auto(gint * laziness) {
    g_thread_create(mplayerc_auto, laziness, TRUE, NULL);
}


gpointer mplayerc_reader(gpointer running) {
    GError * error = NULL;
    gchar * out;
    g_debug("Worker starting...");
    while (TRUE) {
        g_io_channel_read_line(mp_out, &out, NULL, NULL, &error);
        if (error != NULL)
            break;
        if (out == NULL)
            break;
        if (g_str_has_prefix(out, "ANS_pause=")) {
            g_debug("saw a pausing answer %c", out[10]);
            gboolean new_status = (out[10] == 'y');
            if (new_status != paused) {
                g_debug("Pause changed!");
                paused = new_status;
                pause_callback(paused);
            }
            if (new_status != pausing_input) {
                mplayerc_pause(pausing_input);
            }
        } else if (g_str_has_prefix(out, "ANS_time_pos=")) {
            current_time = g_strtod(out+13, NULL);
            g_debug("saw a time answer %f", current_time);
            time_callback(g_string_new(out+13));
        #ifdef DEBUG
        } else {
            printf("%s",out);
        #endif
        }
        g_free(out);
    }
    g_warning("mplayer quit - Exiting...");
    *(gboolean *)running = FALSE;
    return NULL;
}

gboolean mplayerc_start(gchar * mpcmd, gchar * mpopts, gchar * video, void (*tcb)(GString*), void (*pcb)(gboolean status), gboolean * running) {
    GError * error = NULL;
    gint tmp_in;
    gint tmp_out;
    inmutex = g_mutex_new();
    gchar * cmdline;
    gchar ** args;
    
    cmdline = g_strconcat(mpcmd, " ", mpopts, " -slave -quiet ", g_shell_quote(video), NULL);
    g_debug("Command line: %s", cmdline);
    if (!g_shell_parse_argv(cmdline, NULL, &args, NULL)) {
      g_debug("failed command line");
      return FALSE;
    }
    g_spawn_async_with_pipes(NULL, args, NULL, G_SPAWN_SEARCH_PATH, 
                             NULL, NULL, &child_pid, &tmp_in, 
                             &tmp_out, NULL, &error);
    
    if (error != NULL) {
        g_debug("failed launch %s", error->message);
        return FALSE;
    }

    // Bad for windows users, but it'll work! maybe some ifdefs? :/
    mp_in = g_io_channel_unix_new(tmp_in); 
    mp_out = g_io_channel_unix_new(tmp_out);
    
    time_callback = tcb;
    pause_callback = pcb;
    g_thread_create(mplayerc_reader, running, TRUE, NULL);
    return TRUE;
}

